/*****************************************************************************
 * Copyright (c) 2011 CEA LIST.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *
 *		CEA LIST - Initial API and implementation
 *		Bonnabesse Fanch (ALL4TEC) fanch.bonnabesse@alltec.net - Bug 476872
 *
 *****************************************************************************/
package org.eclipse.papyrus.infra.gmfdiag.common.editpolicies;

import java.util.Iterator;
import java.util.Map;

import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.gef.commands.Command;
import org.eclipse.gmf.runtime.common.core.command.CompositeCommand;
import org.eclipse.gmf.runtime.common.core.command.ICommand;
import org.eclipse.gmf.runtime.common.core.command.UnexecutableCommand;
import org.eclipse.gmf.runtime.diagram.core.util.ViewUtil;
import org.eclipse.gmf.runtime.diagram.ui.commands.CommandProxy;
import org.eclipse.gmf.runtime.diagram.ui.commands.CreateCommand;
import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy;
import org.eclipse.gmf.runtime.diagram.ui.editparts.GraphicalEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.ListCompartmentEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editpolicies.CreationEditPolicy;
import org.eclipse.gmf.runtime.diagram.ui.l10n.DiagramUIMessages;
import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequest;
import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequest.ViewDescriptor;
import org.eclipse.gmf.runtime.diagram.ui.requests.EditCommandRequestWrapper;
import org.eclipse.gmf.runtime.emf.commands.core.command.CompositeTransactionalCommand;
import org.eclipse.gmf.runtime.emf.type.core.requests.MoveRequest;
import org.eclipse.gmf.runtime.notation.Node;
import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.papyrus.infra.gmfdiag.common.commands.CreateViewCommand;
import org.eclipse.papyrus.infra.gmfdiag.common.commands.SemanticAdapter;
import org.eclipse.papyrus.infra.services.edit.utils.RequestParameterConstants;

/**
 * Default creation edit policy replacement used to replace {@link CreateCommand} by {@link CreateViewCommand},
 * different implementation of the canExecute() method.
 * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=346739
 */
public class DefaultCreationEditPolicy extends CreationEditPolicy {

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected Command getCreateCommand(CreateViewRequest request) {

		// This overrides getCreatCommand in order to use a specific CreateViewCommand (instead of
		// org.eclipse.gmf.runtime.diagram.ui.commands.CreateCommand.

		// The original CreateCommand#canExecute() implementation rely on ViewProvider#provides(CreateViewForKindOperation op)
		// method to know if a view can be created. The problem is that this method is incorrectly generated by GMF Tooling and should be avoided.

		// CreateViewCommand replace the semantic adapter in its call to ViewService to know if a provider exists.

		TransactionalEditingDomain editingDomain = ((IGraphicalEditPart) getHost()).getEditingDomain();
		CompositeTransactionalCommand cc = new CompositeTransactionalCommand(editingDomain, DiagramUIMessages.AddCommand_Label);

		Iterator<? extends ViewDescriptor> descriptors = request.getViewDescriptors().iterator();
		while (descriptors.hasNext()) {

			CreateViewRequest.ViewDescriptor descriptor = descriptors.next();
			ICommand createCommand = new CreateViewCommand(editingDomain, descriptor, (View) (getHost().getModel()));
			ICommand setBoundsCommand = getSetBoundsCommand(request, descriptor);
			if (setBoundsCommand != null) {
				// Add SetBounds
				createCommand = CompositeCommand.compose(createCommand, setBoundsCommand);
			}
			cc.compose(createCommand);

		}

		return new ICommandProxy(cc.reduce());

	}

	/**
	 * Get a SetBoundsCommand to move a new view at current mouse position.
	 *
	 * @param request
	 *            The creation request.
	 * @param descriptor
	 *            The descriptor of the new element.
	 * @return The set bounds command.
	 */
	protected ICommand getSetBoundsCommand(CreateViewRequest request, CreateViewRequest.ViewDescriptor descriptor) {
		return null;
	}

	/**
	 * @see org.eclipse.gmf.runtime.diagram.ui.editpolicies.CreationEditPolicy#getReparentCommand(org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart)
	 *
	 * @param gep
	 *            the editpart being reparented
	 * @return The CompositeCommand that containing the modified MoveRequest.
	 */
	@Override
	protected ICommand getReparentCommand(IGraphicalEditPart gep) {

		// This overrides getReparentCommand in order to store on the MoveRequest a parameter which indicate where the move action is done

		CompositeCommand cc = new CompositeCommand(DiagramUIMessages.AddCommand_Label);
		View container = (View) getHost().getModel();
		EObject context = ViewUtil.resolveSemanticElement(container);
		View view = (View) gep.getModel();
		EObject element = ViewUtil.resolveSemanticElement(view);

		TransactionalEditingDomain editingDomain = ((IGraphicalEditPart) getHost())
				.getEditingDomain();

		// semantic
		if (null != element) {
			MoveRequest moveRequest = new MoveRequest(editingDomain, context, element);

			// Adding on the request where the action is done.
			Map parameters = moveRequest.getParameters();
			if (null != parameters) {
				// Here, the action is done on the diagram
				parameters.put(RequestParameterConstants.TYPE_MOVING, RequestParameterConstants.TYPE_MOVING_DIAGRAM);
				parameters.put(RequestParameterConstants.AFFECTED_VIEW, view);
			}

			moveRequest.addParameters(parameters);

			Command moveSemanticCmd = getHost().getCommand(
					new EditCommandRequestWrapper(moveRequest));

			if (null == moveSemanticCmd) {
				return org.eclipse.gmf.runtime.common.core.command.UnexecutableCommand.INSTANCE;
			}

			cc.compose(new CommandProxy(moveSemanticCmd));
		}

		// notation
		cc.compose(getReparentViewCommand(gep));
		return cc;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected ICommand getReparentViewCommand(IGraphicalEditPart gep) {
		GraphicalEditPart parent = (GraphicalEditPart) getHost();
		if (parent instanceof ListCompartmentEditPart) {
			if (false == canCreate(gep)) {
				return UnexecutableCommand.INSTANCE;
			}
		}
		return super.getReparentViewCommand(gep);
	}

	/**
	 * {@inheritDoc}
	 */
	private boolean canCreate(IGraphicalEditPart gep) {
		ViewDescriptor descriptor = getViewDescriptor(gep);
		CreateViewRequest request = new CreateViewRequest(descriptor);
		Command createCommand = getCreateCommand(request);
		return createCommand.canExecute();
	}

	/**
	 * {@inheritDoc}
	 */
	private CreateViewRequest.ViewDescriptor getViewDescriptor(IGraphicalEditPart gep) {
		String type = gep.getNotationView().getType();
		IAdaptable elementAdapter = new SemanticAdapter(gep.resolveSemanticElement(), null);
		return new CreateViewRequest.ViewDescriptor(elementAdapter, Node.class, type, ViewUtil.APPEND, false, ((IGraphicalEditPart) getHost()).getDiagramPreferencesHint());
	}
}
